﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace grounding.nao.sensors
{
    public class NaoSensorFrame : EventArgs, ISensorFrame
    {
        protected List<SensorFrame> sensorframes;
        protected Nao nao;
        protected DateTime timestamp;
        private int cachedCount = -1;

        #region Constructors

        public NaoSensorFrame(Nao robot, SensorFrame[] frames) : this(robot, new List<SensorFrame>(frames)) { }
        public NaoSensorFrame(Nao robot, SensorFrame[] frames, DateTime time) : this(robot, new List<SensorFrame>(frames), time) { }
        public NaoSensorFrame(Nao robot, List<SensorFrame> frames) : this(robot, frames, DateTime.Now) { }
        public NaoSensorFrame(Nao robot, List<SensorFrame> frames, DateTime time)
        {
            nao = robot;
            sensorframes = frames;
            timestamp = time;
        }

        #endregion

        

        /// <summary>
        /// Searches for a <c>SensorFrame</c> that was published by the sensor identified
        /// by the <paramref name="name"/> parameter.
        /// </summary>
        /// <param name="name">Name of the sensor to search for</param>
        /// <returns>
        /// SensorFrame that generated by the sensor with the identifier specified in
        /// <paramref name="name"/>
        /// </returns>
        public SensorFrame this[string name]
        {
            get
            {
                foreach (SensorFrame frame in sensorframes)
                {
                    if (frame.Sender.Name == name)
                        return frame;
                }

                throw new ArgumentOutOfRangeException("Couldn't find " + name + " in NaoSensorFrame");
            }
        }

        #region ISensorFrame Members
        
        /// <summary>
        /// Builds a 1D array of the data contained in NaoSensorFrame.
        /// </summary>
        /// <returns>A 1D array of the data contained in NaoSensorFrame</returns>
        public double[] ToArray()
        {
            double[] arr = new double[Count];
            CopyTo(arr, 0);
            return arr;
        }

        public ISensor Sender { get { return nao; } }

        public DateTime Timestamp { get { return timestamp; } }

        public double this[int i]
        {
            get
            {
                int accumulator = i;
                for (int x = 0; x < sensorframes.Count; x++)
                {
                    if (accumulator < sensorframes[x].Count)
                        return sensorframes[x][accumulator];
                    else
                        accumulator -= sensorframes[x].Count - 1;
                }
                throw new IndexOutOfRangeException();
            }
        }

        #endregion

        #region ICollection<double> Members

        public void Add(double item) { throw new InvalidOperationException("NaoSensorFrame is an immutable datastructure"); }

        public void Clear() { throw new InvalidOperationException("NaoSensorFrame is an immutable datastructure"); }

        public bool Contains(double item)
        {
            foreach (SensorFrame frame in sensorframes)
                if (frame.Contains(item))
                    return true;
            return false;
        }

        public void CopyTo(double[] array, int arrayIndex)
        {
            for (int i = 0; i < sensorframes.Count; i++)
                for (int j = 0; j < sensorframes[i].Count; j++)
                    array.SetValue(sensorframes[i][j], arrayIndex++);
        }

        public int Count
        {
            get
            {
                // Cache the count since NaoSensorFrame is immutable
                if (cachedCount == -1)
                {
                    int accumulator = 0;
                    foreach (SensorFrame frame in sensorframes)
                        accumulator += frame.Count;
                    cachedCount = accumulator;
                    return accumulator;
                }
                else
                    return cachedCount;
            }
        }

        public bool IsReadOnly { get { return true; } }

        public bool Remove(double item) { throw new InvalidOperationException("NaoSensorFrame is an immutable datastructure"); }

        #endregion

        #region IEnumerable<double> Members

        public IEnumerator<double> GetEnumerator()
        {
            return new Enumerator(this);
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new Enumerator(this);
        }

        #endregion

        #region Enumerator Implementation

        internal class Enumerator : IEnumerator<double>
        {
            private NaoSensorFrame list;
            private List<SensorFrame>.Enumerator sensorEnumerator;
            private int currentFrameIndex;

            public Enumerator(NaoSensorFrame frame)
            {
                list = frame;
                sensorEnumerator = frame.sensorframes.GetEnumerator();
                currentFrameIndex = 0;
            }

            #region IEnumerator<double> Members

            public double Current { get { return sensorEnumerator.Current[currentFrameIndex]; } }

            #endregion

            #region IDisposable Members

            public void Dispose() { }

            #endregion

            #region IEnumerator Members

            object System.Collections.IEnumerator.Current { get { return sensorEnumerator.Current[currentFrameIndex]; } }

            public bool MoveNext()
            {
                if (currentFrameIndex + 1 < sensorEnumerator.Current.Count)
                {
                    currentFrameIndex++;
                    return true;
                }
                else
                {
                    currentFrameIndex = 0;
                    return sensorEnumerator.MoveNext();
                }
            }

            public void Reset()
            {
                sensorEnumerator = list.sensorframes.GetEnumerator();
                currentFrameIndex = 0;
            }

            #endregion
        }

        #endregion

        #region IFormattable Members

        public override string ToString() { return ToString("", CultureInfo.CurrentCulture); }
        /// <summary>
        /// Prints out a string representation of the NaoSensorFrame.  This ignores the
        /// format string currently
        /// </summary>
        /// <param name="format"></param>
        /// <param name="formatProvider"></param>
        /// <returns></returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            String retVal = "";
            for (int i = 0; i < sensorframes.Count; i++)
            {
                retVal += string.Format("{0,15} : {1}\n", sensorframes[i].Sender.Type.ToString(formatProvider), sensorframes[i].ToString());
            }
            return retVal;
        }

        #endregion
    }
}
